package com.songqi.common.web.wechat;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.songqi.common.core.exception.CustomException;
import com.songqi.common.core.utils.sign.ShaUtil;
import com.songqi.common.redis.RedisUtils;
import com.songqi.common.web.wechat.model.*;
import com.songqi.common.web.wechat.properties.WechatProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: songqi(13776346982)
 * @date: 2023/2/22 22:37
 */
@EnableConfigurationProperties(WechatProperties.class)
@ConditionalOnProperty(prefix = "tencent.wechat", name = {"appid","secret"})
public class WechatUtils {

    private static final Logger logger = LoggerFactory.getLogger(WechatUtils.class);

    @Resource
    private WechatProperties wechatProperties;

    @Resource
    private RedisUtils redisUtils;

    private static final String BASE_STRING = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final String tokenUrl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s";
    private static final String userInfoUrl = "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=%s&code=%s";
    private static final String userDetailUrl = "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail?access_token=%s";
    private static final String corpTicketUrl = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=%s";
    private static final String appTicketUrl = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?type=agent_config&access_token=%s";
    /**
     * 微信登录获取accessToken
     */
    private static final String accessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
    /**
     * 小程序登录获取accessToken
     */
    private static final String jsCode2SessionUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";




    /**
     * 获取企业微信用户userid
     * @param code
     * @return
     */
    public String getQyWxUserId(String code){
        QyWxUserInfo wxUserInfo = getQyWxUserInfo(code);
        if(wxUserInfo != null){
            String userid = wxUserInfo.getUserid();
            if(StrUtil.isBlank(userid)){
                // 提交
                throw new CustomException("您并非该企业用户,请尝试使用账号密码登录");
            }
            return userid;
        }
        return "";
    }


    /**
     * 获取企业微信用户详细信息
     * @param code
     * @return
     */
    public QyWxUserDetail getQyWxUserDetail(String code){
        QyWxUserInfo wxUserInfo = getQyWxUserInfo(code);
        String userTicket = wxUserInfo.getUser_ticket();
        String userid = wxUserInfo.getUserid();
        if(StrUtil.isBlank(userid)){
            // 提交
            throw new CustomException("您并非该企业用户,请尝试使用账号密码登录");
        }
        //获取访问用户敏感信息
        if(StrUtil.isNotBlank(userTicket)){
            String accessToken = this.getQyAccessToken();
            return getQyWxUserDetail(accessToken, userTicket);
        }

        return null;
    }





    public WxConfigRes getJsSign(String url) {
        logger.info("getJsSign:{}, getCorpTicket:{}, corpid:{}, agentsecret:{}", url, corpTicketUrl, wechatProperties.getCorpId(), wechatProperties.getSecret());
        WxConfigRes wxConfigRes = new WxConfigRes();
        try {
            String nonceStr = RandomUtil.randomString(BASE_STRING, 10);
            long timestamp = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
            String source = "jsapi_ticket=" + getCorpTicket() + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url;
            String sha1HexString = ShaUtil.getSha1HexString(source);
            wxConfigRes.setAppId(wechatProperties.getCorpId());
            wxConfigRes.setTimestamp(timestamp);
            wxConfigRes.setNonceStr(nonceStr);
            wxConfigRes.setSignature(sha1HexString);
        } catch (Exception e) {
            logger.error("get wxConfig:", e);
            throw new RuntimeException(e);
        }
        return wxConfigRes;
    }

    public WxConfigRes getJsAgentSign(String url) {
        logger.info("getJsAgentSign:{}, appTicketUrl:{}, corpid:{}, agentid:{}, agentsecret:{}", url, appTicketUrl, wechatProperties.getCorpId(), wechatProperties.getAgentId(), wechatProperties.getSecret());
        WxConfigRes wxConfigRes = new WxConfigRes();
        try {
            String nonceStr = RandomUtil.randomString(BASE_STRING, 10);
            long timestamp = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
            String source = "jsapi_ticket=" + getAppTicket() + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url;
            String sha1HexString = ShaUtil.getSha1HexString(source);
            wxConfigRes.setAppId(wechatProperties.getCorpId());
            wxConfigRes.setAgentId(wechatProperties.getAgentId());
            wxConfigRes.setTimestamp(timestamp);
            wxConfigRes.setNonceStr(nonceStr);
            wxConfigRes.setSignature(sha1HexString);
        } catch (Exception e) {
            logger.error("get wxConfig:", e);
            throw new RuntimeException(e);
        }
        return wxConfigRes;
    }

    private String getAppTicket() {
        String key = "work:app:ticket";
        String accessToken = this.getQyAccessToken();
        String ticketCache = redisUtils.getCacheObject(key);
        if (!StringUtils.hasText(ticketCache)) {
            String url = String.format(appTicketUrl, accessToken);
            HttpResponse response = HttpRequest.get(url).execute();
            if(response.isOk()) {
                String respBody = response.body();
                WxTicketRes wxTicketRes = JSONUtil.toBean(respBody, WxTicketRes.class);
                logger.info("get app ticket: {}", respBody);
                if (Objects.nonNull(wxTicketRes) && StringUtils.hasText(wxTicketRes.getTicket())) {
                    redisUtils.setCacheObject(key, wxTicketRes.getTicket(), wxTicketRes.getExpires_in(), TimeUnit.SECONDS);
                    return wxTicketRes.getTicket();
                }
            }

        }
        return ticketCache;
    }

    private String getCorpTicket() {
        String key = "work:corp:ticket";
        String accessToken = this.getQyAccessToken();
        String ticketCache = redisUtils.getCacheObject(key);
        if (!StringUtils.hasText(ticketCache)) {
            String url = String.format(corpTicketUrl, accessToken);

            HttpResponse response = HttpRequest.get(url).execute();
            if(response.isOk()) {
                String respBody = response.body();
                WxTicketRes wxTicketRes = JSONUtil.toBean(respBody, WxTicketRes.class);
                logger.info("get corp ticket: {}", respBody);
                if (Objects.nonNull(wxTicketRes) && StringUtils.hasText(wxTicketRes.getTicket())) {
                    redisUtils.setCacheObject(key, wxTicketRes.getTicket(), wxTicketRes.getExpires_in(), TimeUnit.SECONDS);
                    return wxTicketRes.getTicket();
                }
            }
        }
        return ticketCache;
    }

    /**
     * 获取企业微信用户身份信息
     * @param code
     * @return
     */
    private QyWxUserInfo getQyWxUserInfo(String code){
        logger.info("授权码：{}", code);
        String accessToken = this.getQyAccessToken();
        if(StrUtil.isBlank(accessToken)){
            throw new CustomException("获取AccessToken失败");
        }
        String url = String.format(userInfoUrl, accessToken, code);
        HttpResponse response = HttpRequest.get(url).execute();
        if(response.isOk()){
            String respBody = response.body();
            logger.info("调用微信获取访问用户身份接口{}返回结果：{}", url, respBody);
            QyWxUserInfo wxUserInfo = JSONUtil.toBean(respBody, QyWxUserInfo.class);
            if(wxUserInfo.getErrcode() == 0 && "ok".equals(wxUserInfo.getErrmsg())){
                return wxUserInfo;
            }else{
                logger.error("调用微信获取访问用户身份接口{}失败。失败信息：{}",
                        url, wxUserInfo.getErrmsg());
            }
        }else {
            logger.error("连接微信超时");
        }
        return null;
    }

    /**
     * 获取微信的accessToken和openId。返回数据结构如下：
     * {
     * "access_token":"ACCESS_TOKEN",
     * "expires_in":7200,
     * "refresh_token":"REFRESH_TOKEN",
     * "openid":"OPENID",
     * "scope":"SCOPE",
     * "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
     * }
     * @param code
     * @return
     */
    public WxAccessToken getWxAccessToken(String code) {
        logger.info("授权码：{}", code);
        String url = String.format(accessTokenUrl, wechatProperties.getAppid(), wechatProperties.getSecret(), code);
        HttpResponse response = HttpRequest.get(url).execute();
        if(response.isOk()){
            String respBody = response.body();
            logger.info("调用微信获取访问用户身份接口{}返回结果：{}", url, respBody);
            WxAccessToken wxAccessToken = JSONUtil.toBean(respBody, WxAccessToken.class);
            if(StrUtil.isBlank(wxAccessToken.getErrmsg())){
                return wxAccessToken;
            }else{
                logger.error("调用微信获取访问用户身份接口{}失败。失败信息：{}",
                        url, wxAccessToken.getErrmsg());
                return wxAccessToken;
            }
        }else {
            logger.error("连接微信超时");
        }
        return null;
    }


    /**
     * 获取小程序accessToken和opneId。返回数据结构：
     * {
     * "openid":"xxxxxx",
     * "session_key":"xxxxx",
     * "unionid":"xxxxx",
     * "errcode":0,
     * "errmsg":"xxxxx"
     * }
     * @param code
     * @return
     */
    public MiniAccessToken getMiniAccessToken(String code) {
        logger.info("授权码：{}", code);
        String url = String.format(jsCode2SessionUrl, wechatProperties.getAppid(), wechatProperties.getSecret(), code);
        HttpResponse response = HttpRequest.get(url).execute();
        if(response.isOk()){
            String respBody = response.body();
            logger.info("调用微信小程序获取访问用户身份接口{}返回结果：{}", url, respBody);
            MiniAccessToken miniAccessToken = JSONUtil.toBean(respBody, MiniAccessToken.class);
            if(miniAccessToken.getErrcode() == 0){
                return miniAccessToken;
            }else{
                logger.error("调用微信获取访问用户身份接口{}失败。失败信息：{}",
                        url, miniAccessToken.getErrmsg());
                return miniAccessToken;
            }
        }else {
            logger.error("连接微信超时");
        }
        return null;
    }


    /**
     * 获取企业access_token。返回数据结构如下：
     * {
     *    "errcode": 0,
     *    "errmsg": "ok",
     *    "access_token": "accesstoken000001",
     *    "expires_in": 7200
     * }
     */
    private String getQyAccessToken(){
        String wechatAccessTokenKey = "weixin.qy.access.token";
        String accessToken = redisUtils.getCacheObject(wechatAccessTokenKey);
        if(StrUtil.isBlank(accessToken)){
            String url = String.format(tokenUrl, wechatProperties.getCorpId(), wechatProperties.getSecret());
            logger.info("tokenUrl: {}", url);
            HttpResponse response = HttpRequest.get(url).execute();
            if(response.isOk()){
                String respBody = response.body();
                logger.info("调用微信获取access_token接口{}返回结果：{}", url, respBody);
                QyWxAccessToken wxAccessToken = JSONUtil.toBean(respBody, QyWxAccessToken.class);
                if(wxAccessToken.getErrcode() == 0 && "ok".equals(wxAccessToken.getErrmsg())){
                    accessToken = wxAccessToken.getAccess_token();
                    int expiresIn = wxAccessToken.getExpires_in();
                    redisUtils.setCacheObject(wechatAccessTokenKey, accessToken, expiresIn, TimeUnit.SECONDS);
                }else{
                    logger.error("调用微信获取access_token接口{}失败。失败信息：{}",
                            url, wxAccessToken.getErrmsg());
                }
            }else {
                logger.error("连接微信超时");
            }
        }
        return accessToken;
    }


    /**
     *
     * 获取访问用户敏感信息。返回数据格式
     * {
     *    "errcode": 0,
     *    "errmsg": "ok",
     *    "userid":"lisi",
     *    "gender":"1",
     *    "avatar":"http://shp.qpic.cn/bizmp/xxxxxxxxxxx/0",
     *    "qr_code":"https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=vcfc13b01dfs78e981c",
     *    "mobile": "13800000000",
     *    "email": "zhangsan@gzdev.com",
     *    "biz_mail":"zhangsan@qyycs2.wecom.work",
     *    "address": "广州市海珠区新港中路"
     * }
     * @param accessToken
     * @param userTicket
     */
    private QyWxUserDetail getQyWxUserDetail(String accessToken, String userTicket) {
        //获取访问用户敏感信息
        String url = String.format(userDetailUrl, accessToken);
        String body = "{\"user_ticket\": \"" + userTicket + "\"}";
        HttpResponse response = HttpRequest.post(url).body(body).execute();
        if(response.isOk()){
            String respBody = response.body();
            logger.info("调用微信获取访问用户敏感信息接口{}返回结果：{}", url, respBody);
            QyWxUserDetail wxUserDetail = JSONUtil.toBean(respBody, QyWxUserDetail.class);
            if(wxUserDetail.getErrcode() == 0 && "ok".equals(wxUserDetail.getErrmsg())){
                return wxUserDetail;
            }else{
                logger.error("调用微信获取访问用户敏感信息接口{}失败。失败信息：{}",
                        url, wxUserDetail.getErrmsg());
            }
        }else {
            logger.error("连接微信超时");
        }
        return null;
    }






}
